package com.doodle.common.utils;

import java.util.LinkedList;

public class MultiObjectPool {
	
	private static final int LRU_SIZE = 5;
	private static final int OBJ_MAX_NUM = 300;
	private static final boolean DEBUG = false;
	
	private LinkedList<ObjectNode> mNodesPool = new LinkedList<ObjectNode>();
	private static MultiObjectPool sInstance;
	private TypeNode mRoot;
	
	private MultiObjectPool() { }
	
	public static MultiObjectPool getInstance() {
		if (sInstance == null) {
			sInstance = new MultiObjectPool();
		}
		sInstance.initRoot();
		return sInstance;
	}
	
	private void initRoot() {
		if (mRoot == null) {
			mRoot = new TypeNode();
		}
	}
	
	public synchronized Object getObject(Class<?> klass) {
		return getObject(klass, null);
	}

	public synchronized Object getObject(Class<?> klass, String key) {
		String name = klass.getName();
		Object obj = null;
		TypeNode tempNode = mRoot.next;
		TypeNode prevNode = mRoot;
		while (tempNode != null) {
			if (tempNode.name.equals(name) && 
					(key == null || key.equals(tempNode.key))) {
				obj = removeObjectNode(tempNode);
				break;
			}
			tempNode = tempNode.next;
			prevNode = prevNode.next;
		}
		if (obj != null && tempNode != mRoot.next) {
			TypeNode placedNode = tempNode.next;
			prevNode.next = placedNode;
			tempNode.next = mRoot.next;
			mRoot.next = tempNode;
		}
		return obj;
	}

	public synchronized void putObject(Object object) {
		putObject(object, null);
	}
	public synchronized void putObject(Object object, String key) {
		String name = object.getClass().getName();
		if (mRoot.next == null) {
			TypeNode typeNode = new TypeNode(name, key);
			addObjectNode(typeNode, object);
			mRoot.next = typeNode;
		} else {
			TypeNode tempNode = mRoot.next;
			TypeNode prevNode = mRoot;
			int count = 1;
			while (tempNode != null && count < LRU_SIZE) {
				if (tempNode.name.equals(name) && 
						(key == null || key.equals(tempNode.key))) {
					addObjectNode(tempNode, object);
					if (tempNode != mRoot.next) {
						TypeNode placedNode = tempNode.next;
						prevNode.next = placedNode;
						tempNode.next = mRoot.next;
						mRoot.next = tempNode;
					}
					return;
				}
				if (count++ < LRU_SIZE) {
					tempNode = tempNode.next;
					prevNode = prevNode.next;
				}
			}
			if (tempNode != null && count >= LRU_SIZE) {
				recycleTypeNode(tempNode);
				prevNode.next = null;
			}
			TypeNode typeNode = new TypeNode(name, key);
			typeNode.next = mRoot.next;
			mRoot.next = typeNode;
			addObjectNode(typeNode, object);
		}
	}
	
	private void addObjectNode(TypeNode typeNode, Object object) {
		if (typeNode.size < OBJ_MAX_NUM) {
			ObjectNode placedNode = typeNode.link;
			ObjectNode objNode;
			if (mNodesPool.size() > 0) {
				objNode = mNodesPool.removeFirst();
			} else {
				objNode = new ObjectNode();
			}
			objNode.object = object;
			typeNode.link = objNode;
			objNode.next = placedNode;
			typeNode.size++;
		}
	}
	
	
	private Object removeObjectNode(TypeNode typeNode) {
		if (typeNode.size > 0) {
			ObjectNode tempNode = typeNode.link.next;
			ObjectNode removedNode = typeNode.link;
			typeNode.link = tempNode;
			typeNode.size--;
			removedNode.next = null;
			mNodesPool.addLast(removedNode);
			return removedNode.object;
		} else return null;
	}

	private void recycleTypeNode(TypeNode node) {
		ObjectNode tempNode = node.link;
		while (tempNode != null) {
			ObjectNode placedNode = tempNode;
			tempNode = tempNode.next;
			placedNode.next = null;
			placedNode = null;
		}
	}
	
	public synchronized void recycleAll() {
		TypeNode typeNode = mRoot.next;
		while (typeNode != null) {
			TypeNode placedNode = typeNode;
			typeNode = typeNode.next;
			recycleTypeNode(placedNode);
			placedNode.next = null;
			placedNode = null;
		}
		mRoot.next = null;
	}
	
	public void traverseAll() {
		TypeNode typeNode = mRoot.next;
		while (typeNode != null) {
			ObjectNode tempNode = typeNode.link;
			int count = 1;
			printLog(typeNode.name + ":");
			while (tempNode != null) {
				tempNode = tempNode.next;
				printLog(count + " ");
				count++;
			}
			printlnLog("");
			typeNode = typeNode.next;
		}
	}
	
	void printLog(String s) {
		if (DEBUG) {
			System.out.print(s);
		}
	}
	void printlnLog(String s) {
		if (DEBUG) {
			System.out.println(s);	
		}
	}

	class TypeNode {
		int size;
		String name;
		String key;
		TypeNode next;
		ObjectNode link;
		
		TypeNode () {
			this(null);
		}
		TypeNode (String name) {
			this(name, null);
		}
		TypeNode (String name, String key) {
			this.name = name;
			this.key = key;
		}
	}
	
	class ObjectNode {
		Object object;
		ObjectNode next;
		ObjectNode prev;
	}
}
